package com.theartofdev.edmodo.cropper;

import ohos.agp.components.Component;
import ohos.agp.components.ScaleInfo;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.resource.*;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Rect;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;
import java.util.Optional;

/**
 * @Author Zhao Baiyi
 *
 * 此类是一个裁剪框工具类
 *
 * 此类提供了一个可以移动，缩放，旋转的裁剪框
 */

public class CropOverlayView {

    //裁剪框
    private Component mCropBound;

    //画笔
    private Paint mPaint;

    //裁剪框画笔颜色
    private Color mClipBoundColor;

    //虚线与否
    private boolean isAntiAlias;

    //画笔宽度
    private float mStrokeWidth;

    //图片的位置
    private int topIndex;

    //位图左边距
    private int marginLeft;

    //位图上边距
    private int marginTop;

    //用来画裁剪框的矩形
    private RectFloat mCropRect;

    //位图工具类
    private PixelMapUtils mPixelMapUtils;

    //屏幕工具类
    private CropWindowHandler mCropWindowHandler;

    //两个位置参数确定图片的左边的位置
    //位置参数，记录图片左上角的位置信息
    private int locationIndex;

    //位置参数2，记录图片左下角的位置信息
    private int locationIndex2;

    //公开出去的一个存有裁剪完的信息的pixelMap
    public static PixelMap croppedPixelMap;

    //构造器，初始化裁剪框对象
    public CropOverlayView(Context context, PixelMapUtils pixelMapUtils, CropWindowHandler cropWindowHandler) {

        //初始化画笔
        mPaint = new Paint();

        //初始化画笔颜色
        mClipBoundColor = Color.WHITE;

        //设置为实线
        isAntiAlias = true;

        //设置画笔宽度
        mStrokeWidth = 5f;

        //设置图片位置，即上边距
        topIndex = 400;

        //设置位置参数
        locationIndex = 0;

        //设置位置参数2
        locationIndex2 = 3;


        //初始化位图工具类
        mPixelMapUtils = pixelMapUtils;

        //初始化windows工具类
        mCropWindowHandler = cropWindowHandler;

        //位图左边距和上边距
        marginLeft = mCropWindowHandler.getWindowWidth() / 2 - mPixelMapUtils.getPixelMapWidth() / 2;
        marginTop = mPixelMapUtils.getPositionTop();

        //初始化记录裁剪框信息的矩形
        mCropRect = mcropRectInit();

        //初始化裁剪框
        mCropBoundInit(context);
    }

    //初始化裁剪框
    private void mCropBoundInit(Context context) {

        //初始化component
        mCropBound = new Component(context);

        //增加drawTask方法
        mCropBound.addDrawTask(drawRect());

        //初始化滑动监听
        setSlideListener();
        //初始化缩放监听
        setScaledListener();
    }

    //初始化记录信息的矩形
    private RectFloat mcropRectInit() {
        //获取pixelmap的宽和高
        int pixelWidth = mPixelMapUtils.getPixelMapWidth();
        int pixelHeight = mPixelMapUtils.getPixelMapHeight();

        //屏幕大小
        int windowWidth = mCropWindowHandler.getWindowWidth();
        int windowHeight = mCropWindowHandler.getWindowHeight();

        /* 计算初始位置
         *  left为屏幕宽度的一半减去图片宽度的一半
         *  因为图片设置为水平居中
         *  top为传进来的值
         */
        int left = windowWidth / 2 - pixelWidth / 4;
        int top = topIndex + pixelHeight / 2 - pixelWidth / 4;
        int right = windowWidth / 2 + pixelWidth / 4;
        int bottom = topIndex + pixelHeight / 2 + pixelWidth / 4;

        RectFloat rectFloat = new RectFloat(left, top, right, bottom);

        return rectFloat;
    }

    //抽象出drawRect方法，以便复用
    private Component.DrawTask drawRect(){
        //DrawTask
        Component.DrawTask drawRect = new Component.DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                draw(canvas, mCropRect);

                //component放大
                component.setScaleX(1f);
                component.setScaleY(1f);
            }
        };
        return  drawRect;
    }

    //给外部提供一个裁剪框
    public Component getmCropBound() {
        return mCropBound;
    }

    //画出裁剪框
    private void draw(Canvas canvas, RectFloat clipBoundRect) {

        //设置画笔参数
        mPaint = new Paint();
        mPaint.setColor(mClipBoundColor);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);

        //画外边框
        //float[] clipBoundPoints = getClipBoundPoints(clipBoundRect);
        canvas.drawCircle((clipBoundRect.right + clipBoundRect.left) / 2, (clipBoundRect.top + clipBoundRect.bottom) / 2, (clipBoundRect.right - clipBoundRect.left) / 2, mPaint);

        drawCorner(canvas, clipBoundRect);

        //画中间线
        mPaint.setStrokeWidth(1);
        float xThird = (clipBoundRect.right - clipBoundRect.left) / 3;
        float yThird = (clipBoundRect.bottom - clipBoundRect.top) / 3;

        canvas.drawLine(new Point(clipBoundRect.left + xThird, clipBoundRect.top),
                new Point(clipBoundRect.left + xThird, clipBoundRect.bottom), mPaint);
        canvas.drawLine(new Point(clipBoundRect.left + xThird * 2, clipBoundRect.top),
                new Point(clipBoundRect.left + xThird * 2, clipBoundRect.bottom), mPaint);
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.top + yThird),
                new Point(clipBoundRect.right, clipBoundRect.top + yThird), mPaint);
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.top + yThird * 2),
                new Point(clipBoundRect.right, clipBoundRect.top + yThird * 2), mPaint);
    }

    //画角落
    public void drawCorner(Canvas canvas, RectFloat clipBoundRect) {
        //画角落
        float cornerWidth = (clipBoundRect.right - clipBoundRect.left) / 15;

        //左上角
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.top),
                new Point(clipBoundRect.left + cornerWidth, clipBoundRect.top), mPaint);
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.top),
                new Point(clipBoundRect.left, clipBoundRect.top + cornerWidth), mPaint);

        //右上角
        canvas.drawLine(new Point(clipBoundRect.right, clipBoundRect.top),
                new Point(clipBoundRect.right - cornerWidth, clipBoundRect.top), mPaint);
        canvas.drawLine(new Point(clipBoundRect.right, clipBoundRect.top),
                new Point(clipBoundRect.right, clipBoundRect.top + cornerWidth), mPaint);

        //左下角
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.bottom),
                new Point(clipBoundRect.left + cornerWidth, clipBoundRect.bottom), mPaint);
        canvas.drawLine(new Point(clipBoundRect.left, clipBoundRect.bottom),
                new Point(clipBoundRect.left, clipBoundRect.bottom - cornerWidth), mPaint);

        //右上角
        canvas.drawLine(new Point(clipBoundRect.right, clipBoundRect.bottom),
                new Point(clipBoundRect.right - cornerWidth, clipBoundRect.bottom), mPaint);
        canvas.drawLine(new Point(clipBoundRect.right, clipBoundRect.bottom),
                new Point(clipBoundRect.right, clipBoundRect.bottom - cornerWidth), mPaint);
    }

    //获得裁剪框的矩形大小
    private float[] getClipBoundPoints(RectFloat clipBoundRect) {
        Point point1 = new Point(clipBoundRect.left, clipBoundRect.top);
        Point point2 = new Point(clipBoundRect.left, clipBoundRect.bottom);
        Point point3 = new Point(clipBoundRect.right, clipBoundRect.bottom);
        Point point4 = new Point(clipBoundRect.right, clipBoundRect.top);

        float[] pointsToDraw = {
                point1.position[0], point1.position[1], point2.position[0], point2.position[1],
                point2.position[0], point2.position[1], point3.position[0], point3.position[1],
                point3.position[0], point3.position[1], point4.position[0], point4.position[1],
                point4.position[0], point4.position[1], point1.position[0], point1.position[1]};
        return pointsToDraw;
    }

    //滑动监听
    public void setSlideListener() {
        //初始化滑动监听
        mCropBound.setTouchEventListener(new Component.TouchEventListener() {
            //创建一个RectFloat用来记录滑动之后的位置
            RectFloat mScrolledClipBoundRect;

            @Override
            public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
                //获得当前手指点击位置
                MmiPoint position = touchEvent.getPointerPosition(0);
                float x = position.getX();
                float y = position.getY();

                //获得当前裁剪框的宽和高
                float width = getCropBoundWidth();
                float height = getCropBoundHeight();

                //获得当前图片的位置
                int left = mPixelMapUtils.getPositionLeft();
                int top = mPixelMapUtils.getPositionTop();
                int right = mPixelMapUtils.getPositionRight();
                int bottom = mPixelMapUtils.getPositionBottom();

                //获得裁剪框位置
                float cropBoundLeft = mCropRect.left;
                float cropBoundTop = mCropRect.top;
                float cropBoundRight = mCropRect.right;
                float cropBoundBottom = mCropRect.bottom;

                //判断裁剪框的位置，裁剪框不能超过图片的边界，并且点击事件必须在裁剪框内才可以移动裁剪框
                if ((right > (x + width / 2)) &&
                        ((x - width / 2) > left) &&
                        (bottom > (y + height / 2)) &&
                        ((y - height / 2) > top)
                        ) {
                    if((cropBoundRight - width/10 > x) &&
                            (x > cropBoundLeft + width/10) &&
                            (cropBoundBottom - height/10> y) &&
                            (y > cropBoundTop + height/10)){
                        mScrolledClipBoundRect = new RectFloat(x - width / 2.0f, y - height / 2.0f, x + width / 2.0f, y + height / 2.0f);

                        updateClipBound(mCropBound, mScrolledClipBoundRect);

                        mCropRect = mScrolledClipBoundRect;
                        return false;
                    }
                }

                return false;
            }
        });
    }

    //放大缩小监听
    public void setScaledListener() {
        //初始化缩放监听
        mCropBound.setScaledListener(new Component.ScaledListener() {
            Point point1;
            Point point2;
            RectFloat mScaledRect;

            @Override
            public void onScaleStart(Component component, ScaleInfo scaleInfo) {
            }

            @Override
            public void onScaleUpdate(Component component, ScaleInfo scaleInfo) {
                float left = 0;
                float right = 0;
                float top = 0;
                float bottom = 0;
                float index = 0;
                if (scaleInfo.horizontalScale != 0) {
                    index = mCropRect.right * (float) Math.sqrt((float) scaleInfo.horizontalScale)
                            - mCropRect.right / 2 - mCropRect.left / 2;
                    right = mCropRect.right * (float) Math.sqrt((float) scaleInfo.horizontalScale);
                    bottom = mCropRect.top / 2 + mCropRect.bottom / 2 + index;
                    left = mCropRect.right / 2 + mCropRect.left / 2 - index;
                    top = mCropRect.top / 2 + mCropRect.bottom / 2 - index;
                } else if (scaleInfo.verticalScale != 0) {
                    index = mCropRect.bottom * (float) Math.sqrt((float) scaleInfo.verticalScale)
                            - mCropRect.bottom / 2 - mCropRect.top / 2;
                    bottom = mCropRect.bottom * (float) Math.sqrt((float) scaleInfo.verticalScale);
                    top = mCropRect.top / 2 + mCropRect.bottom / 2 - index;
                    left = mCropRect.right / 2 + mCropRect.left / 2 - index;
                    right = mCropRect.right / 2 + mCropRect.left / 2 + index;
                }

                //设置判断 剪裁框不能超过图片的范围，也不能剪裁框右边向左滑动超过左边的位置，下边向上滑动不能超过上边的位置
                if (
                        (mPixelMapUtils.getPositionLeft() < left)
                                && (mPixelMapUtils.getPositionTop() < top)
                                && (mPixelMapUtils.getPositionRight() > right)
                                && (mPixelMapUtils.getPositionBottom() > bottom)
                                && (index > mPixelMapUtils.getPixelMapWidth() / 8)) {
                    //初始化滑动后的剪裁框
                    mScaledRect = new RectFloat(left, top, right, bottom);
                    //更新剪裁框
                    // @Param HighlightView clipBound util class
                    // @Param Component mClipBound your crop box
                    // @Param RectFloat mScaledRect a RectFloat which contain your crop box's position message
                    updateClipBound(mCropBound, mScaledRect);
                }

            }

            @Override
            public void onScaleEnd(Component component, ScaleInfo scaleInfo) {
                if (mScaledRect != null) {
                    mCropRect = mScaledRect;
                }
            }

        });
    }

    //更新裁剪框
    private void updateClipBound(Component component, RectFloat clipBoundRect) {

        Component.DrawTask mClipBoundUpdate = new Component.DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                //draw裁剪框
                draw(canvas, clipBoundRect);
            }
        };
        component.release();
        component.addDrawTask(mClipBoundUpdate);
    }

    //裁剪框旋转
    public void rotateOnce(){
        //更新图片的左上角和左下角的位置参数，只有0,1,2,3四种状态
        //左上角和左下角两个点确定一条边

        //旋转使得指数加一
        locationIndex = (locationIndex + 1) % 4;
        //旋转使得指数加一
        locationIndex2 = (locationIndex2 + 1) % 4;

        //更新裁剪框信息
        updateRotateCropRect();

        //释放裁剪框的DrawTask
        mCropBound.release();

        //重新添加DrawTask
        mCropBound.addDrawTask(drawRect());
    }

    //更新旋转后的矩形信息
    private void updateRotateCropRect(){

        //求图片左边空白
        int margin = mCropWindowHandler.getWindowWidth()/2 - mPixelMapUtils.getPixelMapWidth()/2;

        //求更新后的位置
        float leftNew = mPixelMapUtils.getPositionBottom() - mCropRect.bottom + margin;
        float topNew = mCropRect.left - margin + topIndex;
        float rightNew = mPixelMapUtils.getPositionRight() - (mCropRect.top - topIndex);
        float bottomNew = mPixelMapUtils.getPositionBottom() - (mPixelMapUtils.getPositionRight() - mCropRect.right);

        //更新裁剪框
        mCropRect = new RectFloat(leftNew , topNew , rightNew , bottomNew);
    }

    //水平翻转
    public void horizontalFilpOnce(){

        //更新翻转后两个确定图片位置的点的信息
        switch (locationIndex){
            case 0:{
                locationIndex = 1;
                break;
            }
            case 1:{
                locationIndex = 0;
                break;
            }
            case 2:{
                locationIndex = 3;
                break;
            }
            case 3:{
                locationIndex = 2;
                break;
            }
        }
        //更新翻转后两个确定图片位置的点的信息
        switch (locationIndex2){
            case 0:{
                locationIndex2 = 1;
                break;
            }
            case 1:{
                locationIndex2 = 0;
                break;
            }
            case 2:{
                locationIndex2 = 3;
                break;
            }
            case 3:{
                locationIndex2 = 2;
                break;
            }
        }

        //更新裁剪框信息
        updataHorizontalFlipCropRect();

        //释放裁剪框的DrawTask
        mCropBound.release();

        //重新添加DrawTask
        mCropBound.addDrawTask(drawRect());
    }

    //垂直翻转
    public void verticalFilpOnce(){

        //更新翻转信息
        switch (locationIndex){
            case 0:{
                locationIndex = 3;
                break;
            }
            case 1:{
                locationIndex = 2;
                break;
            }
            case 2:{
                locationIndex = 1;
                break;
            }
            case 3:{
                locationIndex = 0;
                break;
            }
        }

        switch (locationIndex2){
            case 0:{
                locationIndex2 = 3;
                break;
            }
            case 1:{
                locationIndex2 = 2;
                break;
            }
            case 2:{
                locationIndex2 = 1;
                break;
            }
            case 3:{
                locationIndex2 = 0;
                break;
            }
        }

        //更新裁剪框信息
        updataVerticalFlipCropRect();

        //释放裁剪框的DrawTask
        mCropBound.release();

        //重新添加DrawTask
        mCropBound.addDrawTask(drawRect());
    }

    //更新水平翻转后的矩形信息
    private void updataHorizontalFlipCropRect(){
        //求图片左边空白
        int margin = mCropWindowHandler.getWindowWidth()/2 - mPixelMapUtils.getPixelMapWidth()/2;

        //求更新后的位置
        float leftNew = mPixelMapUtils.getPositionRight() - mCropRect.right + margin;
        float topNew = mCropRect.top;
        float rightNew = mPixelMapUtils.getPositionRight() -  (mCropRect.left - mPixelMapUtils.getPositionLeft());
        float bottomNew = mCropRect.bottom;

        //更新裁剪框
        mCropRect = new RectFloat(leftNew , topNew , rightNew , bottomNew);
    }

    //更新垂直翻转后的矩形信息
    private void updataVerticalFlipCropRect(){
        //求图片左边空白
        int margin = mCropWindowHandler.getWindowWidth()/2 - mPixelMapUtils.getPixelMapWidth()/2;

        //求更新后的位置
        float leftNew = mCropRect.left;
        float bottomNew = mPixelMapUtils.getPositionBottom() - (mCropRect.top - mPixelMapUtils.getPositionTop());
        float rightNew = mCropRect.right;
        float topNew = mPixelMapUtils.getPositionBottom() - mCropRect.bottom + mPixelMapUtils.getPositionTop();

        //更新裁剪框
        mCropRect = new RectFloat(leftNew , topNew , rightNew , bottomNew);
    }

    //获得裁剪框的宽
    private float getCropBoundWidth() {
        return mCropRect.right - mCropRect.left;
    }

    //获得裁剪框的高
    private float getCropBoundHeight() {
        return mCropRect.bottom - mCropRect.top;
    }

    //取得一个裁剪框参数的矩形，给外部使用
    public RectFloat getmCropRect() {
        return mCropRect;
    }

    //获取裁剪后的pixelmap
    private Optional<PixelMap> getClippedPixelMap(Context context) {
        String path = getPathById(context, mPixelMapUtils.getmId());
        if (path == null || path.isEmpty()) {
            return Optional.empty();
        }
        //loading 加载信息
        RawFileEntry assetManager = context.getResourceManager().getRawFileEntry(path);
        ImageSource.SourceOptions options = new ImageSource.SourceOptions();
        options.formatHint = "image/jpg";

        //decoding
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();


        //创建一个新的矩形
        //此矩形用来记录裁剪框相对于图片的位置信息
        Rect commonClipRect = getRelativeRect();

        //设置解码选项，即矩形的位置信息
        decodingOptions.desiredRegion = commonClipRect;

        try {
            Resource asset = assetManager.openRawFile();
            ImageSource source = ImageSource.create(asset, options);
            return Optional.ofNullable(source.createPixelmap(decodingOptions));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }


    //获得裁剪框相对与图片的位置信息
    private Rect getRelativeRect(){
        //定义几个参数用来计算裁剪框和图片的相对位置信息
        //minX为相对位置的左上角的横坐标
        //minY为相对位置的左上角的纵坐标
        //宽度、高度需要缩放回去正常的大小
        int minX = 0;
        int minY = 0;
        int width = (int) mCropRect.getWidth() * mPixelMapUtils.getRealPixelMapWidth() / mCropWindowHandler.getWindowWidth();
        int height = (int) mCropRect.getHeight() * mPixelMapUtils.getRealPixelMapWidth() / mCropWindowHandler.getWindowWidth();

        /**
         * 根据两个不同的位置指数的信息
         *         来判断现在图片的左边具体在什么位置
         *
         *         图片四个角位置左上角为0 右上角为1 右下角为2 左下角为3
         *         比如左上角的参数locationIndex最开始为0 旋转或翻转之后变为了3
         *         左下角的参数locationIndex2的最开始为3 旋转后变为了1
         *         就可以根据这两个点判断出左边旋转或翻转到了具体哪里
         *
         *         并且根据裁剪框于图片的绝对位置（相对于屏幕）
         *         求得图片与裁剪框的相对位置
         *         以便用来解码图片为pixelmap
         */
        if((locationIndex == 0)&&(locationIndex2 == 3)){
            minX = (int) (mCropRect.left - marginLeft);
            minY = (int) (mCropRect.top - marginTop);
        }else if((locationIndex == 0)&&(locationIndex2 == 1)){
            minX = (int) (mCropRect.top - marginTop);
            minY = (int) (mCropRect.left - marginLeft);
        }else if((locationIndex == 1)&&(locationIndex2 == 0)){
            minX = (int) (mCropRect.top - marginTop);
            minY = (int) (mPixelMapUtils.getPositionRight() - mCropRect.right);
        }else if((locationIndex == 1)&&(locationIndex2 == 2)){
            minX = (int) (mPixelMapUtils.getPositionRight() - mCropRect.right);
            minY = (int) (mCropRect.top - marginTop);
        }else if((locationIndex == 2)&&(locationIndex2 == 1)){
            minX = (int) (mPixelMapUtils.getPositionRight() - mCropRect.right);
            minY = (int) (mPixelMapUtils.getPositionBottom() - mCropRect.bottom);
        }else if((locationIndex == 2)&&(locationIndex2 == 3)){
            minX = (int) (mPixelMapUtils.getPositionBottom() - mCropRect.bottom);
            minY = (int) (mPixelMapUtils.getPositionRight() - mCropRect.right);
        }else if((locationIndex == 3)&&(locationIndex2 == 2)){
            minX = (int) (mPixelMapUtils.getPositionBottom() - mCropRect.bottom);
            minY = (int) (mCropRect.left - marginLeft);
        }else if((locationIndex == 3)&&(locationIndex2 == 0)){
            minX = (int) (mCropRect.left - marginLeft);
            minY = (int) (mPixelMapUtils.getPositionBottom() - mCropRect.bottom);
        }

        //缩放回正常的大小
        minX = minX * mPixelMapUtils.getRealPixelMapWidth() / mCropWindowHandler.getWindowWidth();
        minY = minY * mPixelMapUtils.getRealPixelMapWidth() / mCropWindowHandler.getWindowWidth();

        //返回一个矩形 此矩形为裁剪框相对于图片的位置信息
        Rect rect = new Rect(minX , minY , width , height);

        return rect;
    }

    //根据id获取相对路径
    private String getPathById(Context context, int id) {
        String path = "";
        if (context == null) {
            return path;
        }

        ResourceManager manager = context.getResourceManager();
        if (manager == null) {
            return path;
        }

        try {
            path = manager.getMediaPath(id);

        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return path;
    }

    //计算并求得裁剪后的pixelmap并且赋给一个对外开放的PixelMap
    //即croppedPixelMap，用于返回给用户
    public void croppedPixel(Context context) {
        PixelMap pixelMap = getClippedPixelMap(context).get();
        croppedPixelMap = pixelMap;
    }

    //返回一个裁剪后的pixelmap
    public static PixelMap getCroppedPixelMap() {
        return croppedPixelMap;
    }
}
